package calao;

import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URL;
import java.util.Locale;
import java.util.Timer;
import java.util.TimerTask;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import static javax.servlet.http.HttpServletResponse.SC_NOT_FOUND;


import org.mortbay.io.Buffer;

import calao.csp.ScriptInterpreter;
import calao.util.Logger;
import static calao.util.Logger.*;

public class WebRequest {

	static final int STAT_PERIOD_LENGTH = 5000;
	
	static class StatTask extends TimerTask {
		@Override
		public void run() {
			if (statPeriod.maxCount==0) System.gc();
			else log(DEBUG, "Current load: %d", statPeriod.maxCount);
			statPeriod = new StatPeriod();
		}		
	}

	static class StatPeriod {
		int maxCount = 0;
		void update() {
			if (count>maxCount) maxCount = count;
		}
	}

	private static final Timer statTimer = new Timer(true);

	private static int count = 0;

	private static StatPeriod statPeriod = new StatPeriod();

	private static int nextId = 1;

	static {
		statTimer.scheduleAtFixedRate(new StatTask(), 
				STAT_PERIOD_LENGTH, STAT_PERIOD_LENGTH);
	}

	final long start = System.currentTimeMillis();
	final long id = nextId++;
	final Server server;
	final String wsPath;
	final String resourcePath;
	final HttpServletRequest request;
	final private HttpServletResponse response;
	private ScriptInterpreter scriptInterpreter;

	private Session session;
	long stop = 0;

	WebRequest(Server server, String workspacePath, String resourcePath, 
			HttpServletRequest request, HttpServletResponse response)
			{
		this.server = server;
		this.wsPath = workspacePath;
		this.resourcePath = resourcePath;
		this.request = request;
		this.response = response;
		this.session = Session.getSession(request.getSession(), workspacePath);
		count++;
		statPeriod.update();
			}

	public Session openSession(String alias, String password)
	{
		session = server.openSession(wsPath, alias, password);
		if (session!=null) session.bindTo(request.getSession());
		return session;
	}

	public Workspace getWorkspace() 
	{
		try {
			return session.getWorkspace();
		}
		catch (NullPointerException e) {
			return null;
		}
	}

	public Session getSession() 
	{
		return session;
	}

	public String getResourcePath() 
	{
		return resourcePath;
	}

	public String getWorkspaceName() 
	{
		return wsPath;
	}

	void processed()
	{
		if (stop==0) {
			stop = System.currentTimeMillis();
			count--;
		}
	}

	public static int getCount()
	{
		return count;
	}

	public void redirectTo(String newPath)
	throws IOException
	{
		String newURL = wsPath+WorkspaceServlet.PATH_SEPARATOR+newPath;
		log(DEBUG, "Request %d redirected to %s", id, newURL);
		response.sendRedirect(newURL);
	}

	Locale getLocale()
	{
		return request.getLocale();
	}

	public OutputStream getResponseStream()
	throws IOException
	{
		return response.getOutputStream();
	}

	public void setResponseContentType(String mimeType)
	{
		response.setContentType(mimeType);
	}

	public void respondWithContentFrom(URL url)
	throws IOException 
	{
		if (url.getPath().endsWith(".csp")) {
			Logger.log(DEBUG, "Request %d returns CSP content from %s", 
					id, url);
			startUnicodeHtmlResponse();
			InputStream input = new BufferedInputStream(url.openStream());
			try {
				getScriptInterpreter().eval(input, response.getOutputStream());
			}
			finally {
				input.close();
			}
		}
		else {
			Buffer mimeType = server.getMimeTypes().getMimeByExtension(url.getPath());
			if (mimeType==null) {
				log(WARNING, "Cannot identify MIME type for %s", url);
				respondWithError(SC_NOT_FOUND, "Resource has unknown MIME type");
			}
			else {
				Logger.log(DEBUG, "Request %d returns %s content from %s", 
						id, mimeType, url);
				OutputStream output = response.getOutputStream();
				response.setContentType(mimeType.toString());
				InputStream input = url.openConnection().getInputStream();
				byte[] buffer = new byte[1024];
				int nchars = 0;
				while ((nchars=input.read(buffer))>0) {
					output.write(buffer, 0, nchars);
				}
				input.close();
			}
		}
	}

	public void respondWithError(int statusCode, String msg)
	throws IOException
	{
		log(WARNING, "Request %d fails (%d): %s", id, statusCode, 
				msg!=null? msg : "(no message)");
		if (msg!=null) response.sendError(statusCode, msg);
		else response.sendError(statusCode);
	}

	void respondWith(HtmlPage page)
	throws IOException
	{
		startUnicodeHtmlResponse();
		page.writeTo(response.getWriter());
	}
	
	void startUnicodeHtmlResponse()
	{
		response.setContentType("text/html");
		response.setCharacterEncoding("utf-8");		
	}

	void handleWith(HttpServlet servlet)
	throws ServletException, IOException
	{
		servlet.service(request, response);
	}

	public String getParameter(String name)
	{
		return request.getParameter(name);
	}
	
	public int getIntParameter(String name, int defaultValue)
	{
		String value = getParameter(name);
		return value==null? defaultValue : Integer.parseInt(value);
	}

	long getProcessingTime()
	{
		return stop-start;
	}
	
	ScriptInterpreter getScriptInterpreter()
	{
		if (scriptInterpreter==null) {
			scriptInterpreter = new ScriptInterpreter();
		}
		return scriptInterpreter;
	}

}
